﻿#include <QHBoxLayout>
#include <QFileDialog>
#include <QIcon>
#include <QComboBox>
#include <QLineEdit>
#include <QCompleter>
#include <QPushButton>
#include <QPlainTextEdit>
#include <QCheckBox>
#include <QApplication>
#include <QTimer>
#include <QFileDialog>
#include <QFont>
#include <QFontDialog>
#include <QMetaType>
#include <QMetaEnum>
#include <QElapsedTimer>
#include <QDebug>
#include "JZPropertyWidget.h"
#include "JZPropertyBrowser.h"

//JZPropertyFocusEventFilter
JZPropertyFocusEventFilter::JZPropertyFocusEventFilter(QObject *parent)
    :QObject(parent)
{
}

bool JZPropertyFocusEventFilter::eventFilter(QObject *object, QEvent *event)
{
    switch (event->type())
    {
    case QEvent::FocusIn:
    case QEvent::FocusOut:
    case QEvent::FocusAboutToChange:
    {
        QFocusEvent *fe = static_cast<QFocusEvent *>(event);
        if (fe->reason() == Qt::ActiveWindowFocusReason)
            return false;

        auto main = object->parent();
        while (main)
        {
            if (main->inherits("JZPropertyWidget"))
                break;
            main = main->parent();
        }

        // Forward focus events to editor because the QStyledItemDelegate relies on them                  
        QCoreApplication::sendEvent(main, event);
        break;
    }
    default:
        break;
    }
    return QObject::eventFilter(object, event);
}

//JZPropertyWidget
JZPropertyWidget::JZPropertyWidget(JZProperty *prop)
{
    m_prop = prop;
}

JZProperty* JZPropertyWidget::prop()
{
    return m_prop;
}

//JZPropertyLineWidget
JZPropertyLineWidget::JZPropertyLineWidget(JZProperty *prop)
    :JZPropertyWidget(prop)
{
    m_line = new QLineEdit();
    connect(m_line, &QLineEdit::returnPressed, this, &JZPropertyLineWidget::close);

    QHBoxLayout* l = new QHBoxLayout();
    l->setContentsMargins(0, 0, 0, 0);
    l->addWidget(m_line);
    setLayout(l);

    JZPropertyFocusEventFilter* f = new JZPropertyFocusEventFilter(this);
    setFocusProxy(m_line);
    m_line->installEventFilter(f);

    m_line->setText(m_prop->value().toString());
}

QVariant JZPropertyLineWidget::value()
{
    return m_line->text();
}

//JZPropertyComboBoxWidget
JZPropertyComboBoxWidget::JZPropertyComboBoxWidget(JZProperty *prop,QVariantList valueList, QStringList dispList)
    :JZPropertyWidget(prop)
{
    m_valueList = valueList;
    m_dispList = dispList;

    m_box = new QComboBox();
    m_box->addItems(m_dispList);
    connect(m_box, SIGNAL(currentIndexChanged(int)), this, SLOT(close()));    

    QHBoxLayout* l = new QHBoxLayout();
    l->setContentsMargins(0, 0, 0, 0);
    l->addWidget(m_box);
    setLayout(l);

    JZPropertyFocusEventFilter* f = new JZPropertyFocusEventFilter(this);
    setFocusProxy(m_box);
    m_box->installEventFilter(f);

    QComboBox* box = m_box;    
    QTimer::singleShot(0, [box]() {        
        box->showPopup();        
    });
     
    int index = m_valueList.indexOf(m_prop->value());
    m_box->setCurrentIndex(index);
}

QVariant JZPropertyComboBoxWidget::value()
{       
    int index = m_box->currentIndex();
    return m_valueList[index];
}

//JZPropertySpinBoxWidget
JZPropertySpinBoxWidget::JZPropertySpinBoxWidget(JZProperty *prop)
    :JZPropertyWidget(prop)
{
    m_box = new QSpinBox();

    QHBoxLayout* l = new QHBoxLayout();
    l->setContentsMargins(0, 0, 0, 0);
    l->addWidget(m_box);
    setLayout(l);

    JZPropertyFocusEventFilter* f = new JZPropertyFocusEventFilter(this);
    setFocusProxy(m_box);
    m_box->installEventFilter(f);

    int min = INT_MIN;
    int max = INT_MAX;

    if(prop->dataType() == qMetaTypeId<int8_t>())
    {
        min = SCHAR_MIN;
        max = SCHAR_MAX;
    }
    else if(prop->dataType() == qMetaTypeId<uint8_t>())
    {
        min = 0;
        max = UCHAR_MAX;
    }
    else if(prop->dataType() == qMetaTypeId<int16_t>())
    {
        min = SHRT_MIN;
        max = SHRT_MAX;
    }
    else if(prop->dataType() == qMetaTypeId<uint16_t>())
    {
        min = 0;
        max = USHRT_MAX;
    }
    else if(prop->dataType() == qMetaTypeId<int>())
    {
        min = INT_MIN;
        max = INT_MAX;
    }

    if(m_prop->minValue().isValid())
        min = m_prop->minValue().toInt();
    if(m_prop->minValue().isValid())
        max = m_prop->maxValue().toInt();

    m_box->setRange(min, max);
    m_box->setValue(m_prop->value().toInt());
}

QVariant JZPropertySpinBoxWidget::value()
{
    return m_box->value();
}

QSpinBox* JZPropertySpinBoxWidget::spinBox()
{
    return m_box;
}

//JZPropertyDoubleSpinBoxWidget
JZPropertyDoubleSpinBoxWidget::JZPropertyDoubleSpinBoxWidget(JZProperty *prop)
    :JZPropertyWidget(prop)
{
    m_box = new QDoubleSpinBox();

    QHBoxLayout* l = new QHBoxLayout();
    l->setContentsMargins(0, 0, 0, 0);
    l->addWidget(m_box);
    setLayout(l);

    JZPropertyFocusEventFilter* f = new JZPropertyFocusEventFilter(this);
    setFocusProxy(m_box);
    m_box->installEventFilter(f);
    m_box->setRange(DBL_MIN, DBL_MAX);

    if(m_prop->minValue().isValid())
        m_box->setMinimum(m_prop->minValue().toDouble());
    if(m_prop->minValue().isValid())
        m_box->setMinimum(m_prop->maxValue().toDouble());

    m_box->setValue(m_prop->value().toDouble());
}

QVariant JZPropertyDoubleSpinBoxWidget::value()
{
    return m_box->value();
}

QDoubleSpinBox* JZPropertyDoubleSpinBoxWidget::spinBox()
{
    return m_box;
}

//JZPropertyDialogWidget
JZPropertyDialogWidget::JZPropertyDialogWidget(JZProperty *prop)
    :JZPropertyWidget(prop)
{
    // 创建一个水平布局的容器
    QHBoxLayout* layout = new QHBoxLayout();
    layout->setContentsMargins(0, 0, 0, 0);
    layout->setSpacing(0);

    // 创建输入框
    m_line = new QLineEdit();
    m_line->setReadOnly(true);
    layout->addWidget(m_line);

    // 创建按钮
    m_btn = new QPushButton("...");
    m_btn->setFixedWidth(40);
    layout->addWidget(m_btn);
    setLayout(layout);

    connect(m_btn, &QPushButton::clicked, this, &JZPropertyDialogWidget::onShowDialog);
  
    m_line->setText(m_prop->displayText());
}

void JZPropertyDialogWidget::onShowDialog()
{
    m_value = showFunction(this);
    close();

    QFocusEvent *fe = new QFocusEvent(QEvent::FocusOut);
    QCoreApplication::sendEvent(this, fe);
}

QVariant JZPropertyDialogWidget::value()
{
    return m_value;
}